package com.tianyuan.lims.utils;

import com.google.gson.internal.LinkedTreeMap;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class AlgoCollectionUtils {

    public static <T, K, V> Map<K, List<V>> collectListToMapListOfKeyVal(List<T> list, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valMapper) {
        return collectListToMapListOfKeyVal(list, keyMapper, valMapper, false);
    }

    public static <T, K, V> Map<K, List<V>> collectListToMapListOfKeyVal(List<T> list, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valMapper, boolean linked) {
        if (CollectionUtils.isEmpty(list)) {
            return linked ? new LinkedHashMap<>() : new HashMap<>();
        }

        Map<K, List<V>> result = linked ? new LinkedHashMap<>() : new HashMap<>();
        for (T t : list) {
            K k = keyMapper.apply(t);
            if (null == k) {
                continue;
            }

            if (!result.containsKey(k)) {
                result.put(k, new Vector<>());
            }

            result.get(k).add(valMapper.apply(t));
        }
        return result;
    }

    public static <T, K> Map<K, List<T>> collectListToMapListOfKey(List<T> list, Function<? super T, ? extends K> keyMapper, boolean linked) {
        if (CollectionUtils.isEmpty(list)) {
            return new LinkedHashMap<>();
        }

        Map<K, List<T>> result = linked ? new LinkedHashMap<>() : new HashMap<>();
        for (T t : list) {
            K k = keyMapper.apply(t);
            if (null == k) {
                continue;
            }

            if (!result.containsKey(k)) {
                result.put(k, new Vector<>());
            }

            result.get(k).add(t);
        }
        return result;
    }

    public static <T, K> Map<K, List<T>> collectListToMapListOfKey(List<T> list, Function<? super T, ? extends K> keyMapper) {
        return collectListToMapListOfKey(list, keyMapper, true);
    }

    public static <T> Map<T, List<T>> collectListToMapList(List<T> list, Comparator<? super T> comparator) {
        if (CollectionUtils.isEmpty(list)) {
            return new LinkedHashMap<>();
        }
        return list.stream().collect(
                Collectors.toMap(
                        (o) -> o,
                        (o) -> {
                            List<T> hs = new Vector();
                            hs.add(o);
                            return hs;
                        },
                        (a, b) -> {
                            a.addAll(b);
                            return a;
                        },

                        () -> new TreeMap<>(comparator)
                )
        );
    }

    /**
     * 将一组数据平均分成n组
     *
     * @param source 要分组的数据源
     * @param n      平均分成n组
     * @param <T>
     * @return
     */
    public static <T> List<List<T>> averageAssign(List<T> source, int n) {
        List<List<T>> result = new ArrayList<List<T>>();
        int remainder = source.size() % n;  //(先计算出余数)
        int number = source.size() / n;  //然后是商
        int offset = 0;//偏移量
        for (int i = 0; i < n; i++) {
            List<T> value = null;
            if (remainder > 0) {
                value = source.subList(i * number + offset, (i + 1) * number + offset + 1);
                remainder--;
                offset++;
            } else {
                value = source.subList(i * number + offset, (i + 1) * number + offset);
            }
            result.add(value);
        }
        return result;
    }

    public static <T> Map<T, T> collectListToMap(List<T> list, Comparator<? super T> comparator, boolean linked) {
        if (CollectionUtils.isEmpty(list)) {
            return new LinkedHashMap<>();
        }

        return list.stream().collect(
                Collectors.toMap(
                        (o) -> o,
                        (o) -> o,
                        (a, b) -> a,
                        () -> linked ? new LinkedTreeMap<>(comparator) : new TreeMap<>(comparator)
                )
        );
    }

    public static <T, K> Map<K, T> collectToMap(List<T> list, Function<? super T, ? extends K> keyMapper, boolean linked) {
        if (CollectionUtils.isEmpty(list)) {
            return new LinkedHashMap<>();
        }

        return list.stream().collect(
                Collectors.toMap(
                        keyMapper,
                        (o) -> o,
                        (a, b) -> a, linked ? LinkedHashMap::new : HashMap::new
                ));
    }

    public static <T, K> Map<K, T> collectToMap(List<T> list, Function<? super T, ? extends K> keyMapper) {
        return collectToMap(list, keyMapper, false);
    }

    public static <K, T> void addToMapList(K k, T t, Map<K, List<T>> map) {
        if (!map.containsKey(k)) {
            map.put(k, new Vector<>());
        }
        map.get(k).add(t);
    }

    public static <T, R> List<T> distinctToNew(List<T> src, Function<T, R> comparator) {
        if (CollectionUtils.isEmpty(src)) {
            return new Vector<>();
        }

        Set<R> keys = new LinkedHashSet<>();
        List<T> ret = new Vector<>();

        for (T t : src) {
            R key = comparator.apply(t);

            if (keys.contains(key)) {
                continue;
            }

            keys.add(key);

            ret.add(t);
        }

        return ret;
    }

    public static <T, R> List<T> distinctOnThis(List<T> src, Function<T, R> comparator) {
        List<T> ret = distinctToNew(src, comparator);
        src.clear();
        src.addAll(ret);
        return src;
    }

    public static <T> List<T> distinctToNew(List<T> src, Comparator<T> comparator) {
        if (CollectionUtils.isEmpty(src)) {
            return new Vector<>();
        }

        Map<T, T> tmp = new LinkedTreeMap<>(comparator);
        for ( T t : src ){
            tmp.putIfAbsent(t, t);
        }

        List<T> ret = new Vector<>();
        tmp.forEach((k, v) -> {
            ret.add(k);
        });
        return ret;
    }

    /**
     * 去重分离器
     * @param src
     * @param comparator
     * @return 被去重的内容
     * @param <T>
     */
    public static <T> List<T> distinctSeparate(List<T> src, Comparator<T> comparator) {
        if (CollectionUtils.isEmpty(src)) {
            return new Vector<>();
        }

        List<T> dupList = new Vector<>();

        Map<T, T> tmp = new LinkedTreeMap<>(comparator);
        for ( T t : src ){
            if( tmp.containsKey(t) ){
                dupList.add(t);
            }else{
                tmp.put(t, t);
            }
        }

        src.clear();
        src.addAll(tmp.values());

        return dupList;
    }

    public static <T> List<T> distinctOnThis(List<T> src, Comparator<T> comparator) {
        if (null == src) {
            return null;
        }
        List<T> ret = distinctToNew(src, comparator);
        src.clear();
        src.addAll(ret);
        return src;
    }

    public static <K, E> void putOrCreateListInMap(Map<K, List<E>> map, K key, E obj) {
        List<E> list;
        if (map.containsKey(key)) {
            list = map.get(key);
        } else {
            list = new Vector<>();
            map.put(key, list);
        }
        list.add(obj);
    }

    /**
     * 拷贝一个新的列表
     *
     * @param source
     * @param <T>
     * @return
     */
    public static <T> List<T> copyListToNew(List<T> source) {
        List<T> ret = new Vector<>();
        if (null != source) {
            ret.addAll(source);
        }
        return ret;
    }

    /**
     * 将列表中的某个属性映射到一个新列表
     *
     * @param source
     * @param outGenerator
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> List<R> mapListToList(List<T> source, Function<T, R> outGenerator) {
        List<R> ret = new Vector<>();
        if (source != null) {
            for (T t : source) {
                ret.add(outGenerator.apply(t));
            }
        }
        return ret;
    }





    /**
     * 是不是列表中所有对象的某个属性都为空
     *
     * @param source
     * @param keyGenerator
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> boolean isAllEmptyOfListProperty(List<T> source, Function<T, R> keyGenerator) {
        if (CollectionUtils.isEmpty(source)) {
            return false;
        }

        boolean findNotEmpty = false;
        for (T t : source) {
            if (null == t) {
                continue;
            }

            R r = keyGenerator.apply(t);
            if (null != r) {
                findNotEmpty = true;
                break;
            }
        }

        return !findNotEmpty;
    }

    public static <T> T getMapValue(Map<String, Object> source, String name) {
        if (null == source || source.size() == 0) {
            return null;
        }
        Object obj = source.get(name);
        return (T) obj;
    }



    public static String getStringInArrayDefault(String[] source, int i) {
        return getStringInArrayDefault(source, i, "");
    }

    public static String getStringInArrayDefault(String[] source, int i, String defstr) {
        if (null == source || source.length == 0) {
            return defstr;
        }

        if (i >= source.length) {
            return defstr;
        }

        String str = source[i];

        return StringUtils.defaultString(str, defstr);
    }

    /**
     * 从列表中取出 beginIndex (include) 之后的元素
     *
     * @param from
     * @param beginIndex
     * @param <T>
     * @return
     */
    public static <T> List<T> takeOut(List<T> from, int beginIndex) {
        List<T> ret = new Vector<>();
        if (CollectionUtils.isEmpty(from)) {
            return ret;
        }

        if (beginIndex < 0) {
            beginIndex = 0;
        }

        for (int i = from.size() - 1; i >= beginIndex; i--) {
            ret.add(from.remove(i));
        }
        return ret;
    }

    /**
     * 将map的值 映射为列表
     *
     * @param map
     * @param <K>
     * @param <T>
     * @return
     */
    public static <K, T> List<T> mapValuesToList(Map<K, T> map) {
        Vector<T> ret = new Vector<>(map.size());

        map.forEach((k, v) -> {
            ret.add(v);
        });

        return ret;
    }

    /**
     * asList
     *
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> List<T> toList(T... obj) {
        List<T> result = new Vector<>();
        CollectionUtils.addAll(result, obj);
        return result;
    }

    public static <T> List<T> toList(Predicate<T> predicate, T... obj) {
        List<T> result = new Vector<>();
        for ( T t : obj ){
            if( predicate.test(t) ){
                result.add(t);
            }
        }
        return result;
    }

    public static <T> List<T> toListByEntity(T obj) {
        List<T> result = new Vector<>();
        result.add(obj);
        return result;
    }

    public static <T, K extends T> List<T> toList(Collection<K> collection) {
        if(null != collection){
            List<T> result = new Vector<>(collection.size());
            CollectionUtils.addAll(result, collection);
            return result;
        }else{
            return new Vector<>();
        }
    }

    public static <T, K extends T> List<T> toList(Predicate<K> predicate, Collection<K> collection) {
        List<T> result = new Vector<>(null != collection ? collection.size() : 0);
        for ( K t : collection ){
            if( predicate.test(t) ){
                result.add(t);
            }
        }
        return result;
    }

    public static <T> Set<T> toSet(Collection<T> collection) {
        return toSet(collection, false);
    }

    public static <T> Set<T> toSet(T ... items) {
        Set<T> result = new HashSet<>();
        for ( T t : items){
            result.add(t);
        }
        return result;
    }

    public static <T> Set<T> toSet(Collection<T> collection, boolean linked) {
        Set<T> result = linked ? new LinkedHashSet<>() : new HashSet<>();
        if (null != collection) {
            CollectionUtils.addAll(result, collection);
        }
        return result;
    }

    /**
     * 两个集合是否有交集
     *
     * @param a
     * @param b
     * @param <T>
     * @return
     */
    public static <T> boolean hasIntersection(Set<T> a, Set<T> b) {
        if (CollectionUtils.isEmpty(a) || CollectionUtils.isEmpty(b)) {
            return false;
        }

        Iterator<T> ait = a.iterator();
        while (ait.hasNext()) {
            if (b.contains(ait.next())) {
                return true;
            }
        }

        Iterator<T> bit = b.iterator();
        while (bit.hasNext()) {
            if (a.contains(bit.next())) {
                return true;
            }
        }

        return false;
    }

    /**
     * 列表类型转换
     *
     * @param ts
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T extends R, R> List<R> caseList(List<T> ts) {
        List<R> rs = new Vector<>();
        if (CollectionUtils.isNotEmpty(ts)) {
            for (T t : ts) {
                rs.add(t);
            }
        }
        return rs;
    }

    public static <T> Set<T> collectListToSetOfKey(List<T> list) {
        Set<T> result = new HashSet<>();
        if (CollectionUtils.isEmpty(list)) {
            return result;
        }
        for (T t : list) {
            if (null == t) {
                continue;
            }

            if (!result.contains(t)) {
                result.add(t);
            }
        }
        return result;
    }

    public static <T, K> Set<K> collectListToSetOfKey(List<T> list, Function<T, K> keyGenerator) {
        Set<K> result = new HashSet<>();
        if (CollectionUtils.isEmpty(list)) {
            return result;
        }
        for (T t : list) {
            K key = keyGenerator.apply(t);
            if (null == key) {
                continue;
            }
            result.add(key);
        }
        return result;
    }

    public static <K, V> void putAll(Map<K, V> to, Map<K, V> added) {
        if (null == added) {
            return;
        }

        to.putAll(added);
    }

    public static <T> void addAll(Collection<T> to, Collection<T> added) {
        if (null == added||null == to) {
            return;
        }
        to.addAll(added);
    }
    public static <T> void addIfNotNull(Collection<T> to, T added) {
        if (null == added||null == to) {
            return;
        }
        to.add(added);
    }

    /**
     * 获取列表中的第几个元素，范围外的返回NULL
     *
     * @param list
     * @param i
     * @param <T>
     * @return
     */
    public static <T> T getInList(List<T> list, int i) {
        if ( i < 0 || null == list || i >= list.size() ) {
            return null;
        }
        return list.get(i);
    }

    public static <T> T getAndRemoveInList(List<T> list, int i) {
        if (i >= list.size()) {
            return null;
        }
        return list.remove(i);
    }

    public static <T> List<T> subList(List<T> list, int begin) {
        return subList(list, begin, -1);
    }

    /**
     * @param list
     * @param begin include
     * @param end   exclude
     * @param <T>
     * @return
     */
    public static <T> List<T> subList(List<T> list, int begin, int end) {
        if( begin < 0 ){
            begin = 0;
        }
        if( end < 0 || end > list.size() ){
            end = list.size();
        }

        if (begin >= list.size()) {
            return Collections.emptyList();
        }

        return list.subList(begin, end);
    }


    public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    /**
     * 把所有实体放到 Key -> List 的Map 中
     *
     * @param mapTo
     * @param entities
     * @param tkFunction
     * @param <K>
     * @param <T>
     */
    public static <K, T> void putEntityToMapList(Map<K, List<T>> mapTo, List<T> entities, Function<T, K> tkFunction) {
        for (T entity : entities) {
            K k = tkFunction.apply(entity);
            if (!mapTo.containsKey(k)) {
                mapTo.put(k, new Vector<>());
            }
            mapTo.get(k).add(entity);
        }
    }

    public static <K, T> void putEntityToMapList(Map<K, List<T>> mapTo, T entity, K key) {
        if (!mapTo.containsKey(key)) {
            mapTo.put(key, new Vector<>());
        }
        mapTo.get(key).add(entity);
    }

    public static <K, T> void putEntityToMap(Map<K, T> mapTo, T entity, K key) {
        if( !mapTo.containsKey(key) ){
            mapTo.put(key, entity);
        }
    }

    public static <K, V, T> void putEntityToMapMapIfAbsent(Map<K, Map<V, T>> mapTo, T entity, K key, V v) {
        if (!mapTo.containsKey(key)) {
            mapTo.put(key, new LinkedHashMap<>());
        }
        mapTo.get(key).putIfAbsent(v, entity);
    }

    public static <T> T[] addToArrayUniqueEle( T[] array, T... items ) {
        if( null == items ){
            return array;
        }

        Set<T> s = AlgoCollectionUtils.toSet(AlgoCollectionUtils.toList(array), true);
        for (T t : items) {
            s.add(t);
        }

        List<T> l = AlgoCollectionUtils.toList(s);
        return l.toArray(array);
    }

    public static <T> List<T> addToListUniqueEle( List<T> array, T... items ) {
        if( null == items ){
            return array;
        }

        Set<T> s = AlgoCollectionUtils.toSet(AlgoCollectionUtils.toList(array), true);
        for (T t : items) {
            s.add(t);
        }

        List<T> l = AlgoCollectionUtils.toList(s);
        return l;
    }

    public static <T> List< List<T> > split(List<T> items, int count){
        if( null == items || items.size() == 0 ){
            return new Vector<>();
        }
        int step = items.size() / count;
        if( step*count < items.size() ){
            step+=1;
        }
        return ListUtils.partition(items, step);
    }

    public static  <T> boolean setEquals(Set<T> o1, Set<T> o2 ){
        if( null == o1 || null == o2 || o1.size() != o2.size() ){
            return false;
        }
        return o1.containsAll(o2);
    }

    /**
     * 按照 value 的长度排序 KEY
     */
    public static <K, E> List<K> orderKeyByValueLength(Map<K, List<E>> map, boolean desc){
        List<K> ks = new Vector<>();
        for ( K entry : map.keySet() ){
            ks.add(entry);
        }
        Collections.sort(ks, Comparator.comparing(k -> map.get(k).size()));
        if(desc){
            Collections.reverse(ks);
        }
        return ks;
    }
}
